package main

import (
	"fmt"
	"strconv"
	"sync"
	"time"
)

/**
【1】管道的遍历:管道支持for-range的方式进行遍历，请注意两个细节
	1)在遍历时，如果管道没有关闭，则会出现deadlock的错误
	2)在遍历时，如果管道已经关闭，则会正常遍历数据，遍历完后，就会退出遍历。

【2】当管道只写入数据，没有读取，达到管道容量之后就会出现阻塞
【3】当管道写的快，读的慢(管道读写频率不一致)，不会出现阻塞问题
**/

func main() {
	//test()
	//test2()
	//test3()
	test4()
}

func test4() {
	// 当管道只写入数据，没有读取，达到管道容量之后就会出现阻塞
	// fatal error: all goroutines are asleep - deadlock!
	var intChan chan int = make(chan int, 10)
	for i := 0; i < 100; i++ {
		intChan <- i
		fmt.Println("写入后长度：", len(intChan))
	}
	fmt.Println("管道的长度：", len(intChan))
}

// 只读或只写的管道
func test3() {

	// 正常可读可写的管道
	//var intChan chan int = make(chan int, 100)

	// 只写管道  chan<-
	var intChan2 chan<- int = make(chan int, 10)
	intChan2 <- 1
	//num := <- intChan2 // invalid operation: cannot receive from send-only channel intChan2 (variable of type chan<- int)

	fmt.Println("管道的长度：", len(intChan2))
	// 只读管道
	var intChan3 <-chan int = make(chan int, 10)

	if len(intChan3) > 0 {
		num := <-intChan3
		fmt.Println(num)
	}
	//intChan3 <- 1 // invalid operation: cannot send to receive-only channel intChan3 (variable of type <-chan int)
	//fmt.Println("管道的长度：", len(intChan3))

}

func test() {

	ch := make(chan int, 100)
	for i := 0; i < 100; i++ {
		ch <- i
	}

	fmt.Println("管道的长度：", len(ch))
	fmt.Println("管道的容量：", cap(ch))

	//在遍历前，如果没有关闭管道，就会出现deadlock的错误
	//所以我们在遍历前要进行管道的关闭
	close(ch)
	for i := range ch {
		fmt.Println(i)
		//close(ch) // panic: close of closed channel
	}
}

/**
请完成协程和管道协同工作的案例，具体要求:
1)开启一个writeData协程，向管道中写入50个整数.
2)开启一个readData协程，从管道中读取writeData写入的数据,
3)注意: writeData和readDate操作的是同一个管道
4)主线程需要等待writeData和readDate协程都完成工作才能退出
*/

var wg sync.WaitGroup

func test2() {
	var testChan chan int = make(chan int, 5)
	wg.Add(2)
	go writeData(testChan)
	go readData(testChan)
	wg.Wait()
	fmt.Println("协程和管道协同工作完成")
	fmt.Println("testChan的容量", cap(testChan))
	fmt.Println("testChan的长度", len(testChan))
}

// 有点类似于生产者
func writeData(testChan chan int) {
	defer wg.Done()
	for i := 0; i < 200; i++ {
		fmt.Println("--------------------------------------")
		testChan <- i
		fmt.Println("testChan写入长度=", len(testChan))
		fmt.Println("写入的数据：" + strconv.Itoa(i))
		time.Sleep(100 * time.Millisecond)
		fmt.Println("testChan睡眠后长度--", len(testChan))
	}
	close(testChan)
}

// 有点类似于消费者
func readData(testChan chan int) {
	defer wg.Done()
	for i := range testChan {
		fmt.Println("管道读出的数据：" + strconv.Itoa(i))
		time.Sleep(5000 * time.Millisecond)
	}
}
